#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <ctime>

// using namespace std;
using std::cin;
using std::cout;
using std::endl;
using std::queue;
using std::string;
using std::vector;

static const int gmaxcap = 5;
template <class T>
class BlockingQueue
{
public:
    BlockingQueue(const int &max_size = gmaxcap)
        : _max_size(max_size)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_pcond,nullptr);
        pthread_cond_init(&_ccond, nullptr);
    }
    void push(const T &in)
    {
        pthread_mutex_lock(&_mutex);
        // if(is_full())
        // 不能用if，要使用while
        while (is_full())
        {
            // pthread_cond_wait函数的第二个参数，必须是我们正在使用的互斥锁！
            // 不然我们线程在条件变量下等待的时候抱着锁直接跑了，其他线程都不能申请成功锁了
            pthread_cond_wait(&_pcond, &_mutex);
            // a. pthread_cond_wait: 该函数调用的时候，会以原子性的方式，将锁释放，并将自己挂起
            // b. pthread_cond_wait: 该函数在被唤醒返回，继续向下执行代码的时候，会自动的重新获取你传入的锁
        }
        // 这里队列肯定没有满
        _q.push(in);
        // pthread_cond_signal：这个函数，可以放在临界区内部，也可以放在外部，也就是unlock下面也可以
        pthread_cond_signal(&_ccond); // 唤醒一个消费者
        pthread_mutex_unlock(&_mutex);
    }
    void pop(T *out)
    {
        pthread_mutex_lock(&_mutex);
        while (is_empty())
        {
            pthread_cond_wait(&_ccond, &_mutex);
        }
        // 走到这里队列一个有数据
        *out = _q.front(); // 拿到队头数据
        _q.pop();
        pthread_cond_signal(&_pcond); // 唤醒一个生产者
        pthread_mutex_unlock(&_mutex);
    }
    ~BlockingQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_pcond);
        pthread_cond_destroy(&_ccond);
    }

private:
    bool is_empty()
    {
        return _q.empty();
    }
    bool is_full()
    {
        return _q.size() == _max_size;
    }

private:
    queue<T> _q;
    int _max_size; // 队列最大容量
    pthread_mutex_t _mutex;
    pthread_cond_t _pcond; // 生产者的条件变量
    pthread_cond_t _ccond; // 消费者的条件变量
};